#include "TBuffer.h"
#include <stdio.h>
#include <string.h>

struct STBlock
{
    char buffer[BLOCK_SIZE];
};
TBufferPool::TBufferPool()
{
    allBlocks_ = new STBlock[BLOCK_COUNT];
    for (int i=0; i<BLOCK_COUNT; i++) noUseBlockNo_.push_back(i);
}

TBufferPool::~TBufferPool()
{
    delete[] allBlocks_;
}

int TBufferPool::AllocBlock()
{
    int blockNo = noUseBlockNo_.back();
    noUseBlockNo_.pop_back();
    return blockNo;
}

void TBufferPool::FreeBlock(int blockNo)
{
    noUseBlockNo_.push_back(blockNo);
}

STBlock* TBufferPool::GetBlock(int blockNo)
{
    STBlock* st = &allBlocks_[blockNo];
    return st;
}

TBuffer::TBuffer(TBufferPool* pool) : readIdx_(0), writeIdx_(0), readNo_(-1), writeNo_(-1)
{
    bufferPool_ = pool;
}

TBuffer::~TBuffer()
{
    for (int i=0; i<blocks_.size(); i++)
    {
        bufferPool_->FreeBlock(blocks_[i]);
    }
}

bool TBuffer::Read(char* buff, int len, bool del)
{
    if (blocks_.empty() || len <= 0) return false;
    if ((writeNo_*BLOCK_SIZE+writeIdx_) - (readNo_*BLOCK_SIZE+readIdx_) < len) return false;
    int oldReadNo = readNo_, oldReadIdx = readIdx_;
    for (; len > 0;)
    {
        STBlock* st = bufferPool_->GetBlock(blocks_[readNo_]);
        int ableReadLen = writeNo_>readNo_?BLOCK_SIZE-readIdx_:writeNo_==readNo_?writeIdx_-readIdx_:0;
        ableReadLen = ableReadLen>len?len:ableReadLen;
        if (ableReadLen > 0)
        {
            memcpy(buff, &st->buffer[readIdx_], ableReadLen);
            buff += ableReadLen;
            len -= ableReadLen;
            readIdx_ += ableReadLen;
            if (readIdx_ == BLOCK_SIZE)
            {
                readNo_++;
                readIdx_ = 0;
            }
        }
    }
    if (del)
    {
        for (;readNo_>0 || (0==readNo_ && 0==writeNo_&&readIdx_==writeIdx_);)
        {
            readNo_--;            writeNo_--;
            bufferPool_->FreeBlock(*blocks_.begin());
            blocks_.erase(blocks_.begin());
        }
    }
    else
    {
        readNo_ = oldReadNo;
        readIdx_ = oldReadIdx;
    }
    return true;
}

bool TBuffer::Write(char* buff, int len)
{
    if (blocks_.empty())
    {
        blocks_.push_back(bufferPool_->AllocBlock());
        readNo_ = 0;        writeNo_ = 0;
    }
    for (; len > 0;)
    {
        STBlock* st = bufferPool_->GetBlock(blocks_[writeNo_]);
        int ableWriteLen = BLOCK_SIZE-writeIdx_;
        ableWriteLen = ableWriteLen>len?len:ableWriteLen;
        if (ableWriteLen > 0)
        {
            memcpy(&st->buffer[writeIdx_], buff, ableWriteLen);
            buff += ableWriteLen;
            len -= ableWriteLen;
            writeIdx_ += ableWriteLen;
            if (writeIdx_ == BLOCK_SIZE && writeNo_ == blocks_.size()-1)
            {
                blocks_.push_back(bufferPool_->AllocBlock());
                writeNo_++;
                writeIdx_ = 0;
            }
        }
    }
    return true;
}

void TBuffer::WriteFromSock(int srcfd, int len)
{

}